"""
wow.api
=======

"""

import urllib2

from wow.core import *
from wow.exceptions import *
from wow.apis.character import Character
from wow.apis.item import Item
import xmltramp

class API(object):
    """
    API
    ===
    
    The "core" of the library, which links all the APIs (in the
    ``apis`` package) together in an ``API`` class. It is
    recommended that all interaction be done using this class.
    
    """
    def __init__(self, region=None, realm=None):
        """
        API.__init__(self, region=None, realm=None) -> API
        Initialise the API wrapper. If ``region`` and/or ``realm``
        is specified, this will be used as the default for all
        methods - note that this is not required, and can be
        overridden at any time, including at runtime.
        
        """
        if region:
            assert self.validate_region(region), InvalidArgument('%s is not a valid World of Warcraft region, or is unsupported by wow-python.' % region)
        self.region = region
        
        if realm:
            assert self.validate_realm(region, realm), InvalidArgument('%s-%s is not a valid World of Warcraft realm.' % (region.upper(), realm.capitalize()))
        self.realm = realm
        
    def search_chars(self, name, region=None, key=None):
        """
        search_chars(self, name, region=None, key=None) -> generator
        Search the Armory for characters with the name ``name``.
        ``key`` is an optional argument which can be used to filter
        the results, commonly in the form of an anonymous ``lambda``
        function.

        """
        region = region or self.region
        assert region, InvalidArgument("No ``region`` argument was supplied for method ``search_chars`` of a ``wow.API`` instance which doesn't have a default (fallback) region set.")
        tree = self.retrieve_data(region, 'search', {'searchQuery': name, 'searchType': 'characters'})
        chars = tree.armorySearch.searchResults.characters._dir
        def _inner():
            for char in chars:
                char_obj = Character(char._attrs, api=self, region=region)
                if (key and key(char_obj)) or not key:
                    yield char_obj
        
        return _inner()
    
    def get_char(self, name, realm=None, region=None, load_reputation=False, load_skills=False, load_talents=False, load_arena_teams=False):
        """
        get_char(self, name, realm=None, region=None,
            load_reputation=False, load_skills=False, 
            load_talents=False, load_arena_teams=False) 
            -> wow.Character
        Loads the full view of a character, as per its own page in
        the Armory. Note that each of the ``load_*`` arguments add
        significant overhead (each of them requires for the API to
        load an additional Armory page), and hence should only be
        ``True`` if absolutely necessary.
        
        Note that the character sheet is (for now) always loaded.
        This will probably be replaced by a better method in future
        revisions.
        
        """
        load_sheet = True  # todo: un-implement enforcing loading of the sheet
        region = region or self.region
        realm = realm or self.realm
        assert region, InvalidArgument("No ``region`` argument was supplied for method ``get_char`` of a ``wow.API`` instance which doesn't have a default (fallback) region set.")
        assert realm, InvalidArgument("No ``realm`` argument was supplied for method ``get_char`` of a ``wow.API`` instance which doesn't have a default (fallback) realm set.")
        
        char = Character({'name': name.capitalize(), 'realm': realm}, api=self, region=region)
        char.load_sheet()
        if load_reputation:
            char.load_reputation()
        if load_skills:
            char.load_skills()
        if load_talents:
            char.load_talents()
        if load_arena_teams:
            char.load_arena_teams()
        
        return char
    
    def get_item(self, id, region=None, load_additional=False):
        """
        get_item(self, id, load_additional=False) -> wow.Item
        Retrieve basic information about the item with the id ``id``,
        and return it in the form of a ``wow.Item`` object. If
        ``load_additional`` is ``True``, information about
        disenchantment and vendor pricing is also retrieved (uses
        one extra query).
        
        """
        region = region or self.region
        assert region, InvalidArgument("No ``region`` argument was supplied for method ``get_char`` of a ``wow.API`` instance which doesn't have a default (fallback) region set.")
        
        tooltip = self.retrieve_data(region, 'item-tooltip', {'i': id})
        info = load_additional and self.retrieve_data(region, 'item-info', {'i': id}) or None
        
        return Item(tooltip, info, self, region)
        

    def retrieve_data(self, region, action, query_string={}):
        """
        retrieve_data(self, region, action, query_string={}) -> None
        Retrieve data from the World of Warcraft Armory, storing it
        in the class. ``action`` is the name of an action on the
        Armory, such as ``character-sheet`` (``.xml`` not included).
        ``query_string`` will be passed as a regular HTTP query
        string.
        
        This method will, using ``urllib2``, fake its user agent in
        order to retrieve a beautiful XML output instead of a
        horribly unparsable HTML ditto.
        
        """
        url = armory_url % {'region': region, 'action': action, 'query': '&'.join('%s=%s' % (key, value) for key, value in query_string.items())}
        
        request = urllib2.Request(url, headers={'User-Agent': fake_user_agent})
        opener = urllib2.build_opener()
        return xmltramp.seed(opener.open(request))

    def validate_region(self, region):
        """
        validate_region(self, region) -> bool
        Check whether a passed region (such as 'eu' or 'us') is
        valid in accordance with Blizzard naming rules.
        
        """
        return region.lower() in regions
    
    def validate_realm(self, region, realm):
        """
        validate_realm(self, region, realm) -> bool
        Validate a region/realm combination against the list of
        realms.
        
        """
        return True  # todo: implement
